/*
 * Copyright (c) 2010-2011, Petr Panteleyev <petr@panteleyev.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.livejournal;

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreePath;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableModel;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.fotobilder.FBAccount;
import org.panteleyev.fotobilder.FBGallery;
import org.panteleyev.livejournal.events.PhotosUpdatedEvent;
import org.panteleyev.swing.BasePanel;
import org.panteleyev.swing.MenuProvider;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.swing.TableUtil;
import org.panteleyev.swing.ToolBarProvider;
import org.panteleyev.utilities.Updatable;

/**
 *
 * @author Petr Panteleyev <petr@panteleyev.org>
 */
public class FotoBilderListPanel extends BasePanel<FotoBilderFrame> implements Updatable, MenuProvider, ToolBarProvider {
    private LJUserProfile       profile;
    private JXTreeTable         galleryTreeTable;
    private JScrollPane         scroller;
    private ArrayList<AbstractAction> toolbarActions = new ArrayList<AbstractAction>();
    private JMenu               menu;

    /* Actions */
    private AbstractAction      updateGalleriesAction;
    private AbstractAction      updatePicturesAction;
    private AbstractAction      openGalleryExternalAction;
    private AbstractAction      createGalleryAction;


    private TreeTableModel          treeTableModel;
    private FBAccount               fbAccount;
    private ArrayList<GalleryInfo>  info = new ArrayList<GalleryInfo>();

    private static DefaultTreeTableModel emptyModel = new DefaultTreeTableModel();

    private Runnable refreshTable = new Runnable() {
        @Override
        public void run() {
            galleryTreeTable.setTreeTableModel(emptyModel);
            galleryTreeTable.setTreeTableModel(treeTableModel);
            galleryTreeTable.expandAll();
            TableUtil.adjustTableColumnSizes(galleryTreeTable, 0, scroller);
            setDefaultStatusLabel();
        }
    };

    private Runnable clearCursorThread = new Runnable() {
        @Override
        public void run() {
            getFrame().finishLongOperation();
        }
    };

    private Runnable updateGalleryThread = new Runnable() {
        @Override
        public void run() {
            try {
                fbAccount.fetchGalsTree();
                info.clear();
                buildInfo(fbAccount.getRootGalleries(), 0);
        //        buildInfo(_fbAccount.getNotLinkedGalleries());
                SwingUtilities.invokeLater(refreshTable);
            } catch (Exception e) {
                MainClass.handleException(getFrame(), e);
            } finally {
                SwingUtilities.invokeLater(clearCursorThread);
            }
        }
    };

    private Runnable updatePicturesThread = new Runnable() {
        @Override
        public void run() {
            try {
                fbAccount.fetchPics();
                getProfile().storePictures(fbAccount);
                getProfile().sendApplicationEvent(new PhotosUpdatedEvent(this));
                setDefaultStatusLabel();
            } catch (Exception e) {
                MainClass.handleException(getFrame(), e);
            } finally {
                SwingUtilities.invokeLater(clearCursorThread);
            }
        }
    };

    private class CreateGalleryThread extends Thread {
        private FBGallery parent;
        private String name;
        private int security;
        private Date date;

        CreateGalleryThread(FBGallery parent, String name, Date date, int sec) {
            this.parent = parent;
            this.name = name;
            this.security = sec;
            this.date = date;
            getFrame().startLongOperation();
        }

        @Override
        public void run() {
            try {
                fbAccount.createGallery(parent,  name, date, security);
                updateGalleryThread.run();
            } catch (IOException e) {
                MainClass.handleException(getFrame(), e);
            } finally {
                SwingUtilities.invokeLater(clearCursorThread);
            }
        }
    }

    private static String[] headerNames = {
        "Name",
        "Images",
        "Security",
        "Gallery Date",
    };


    public FotoBilderListPanel(FotoBilderFrame frame, LJUserProfile profile) {
        super(frame);
        this.profile = profile;
        fbAccount = profile.getFbAccount();

        createActions();
        initComponents();

        setDefaultStatusLabel();
        updateGalleries();
    }

    private LJUserProfile getProfile() {
        return profile;
    }


    private void createActions() {
        updateGalleriesAction = new AbstractAction(L10N.FBF_UPDATE_GALLERIES_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                updateGalleries();
            }
        };

        updatePicturesAction = new AbstractAction(L10N.FBF_UPDATE_PICTURES_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onUpdatePictures();
            }
        };

        openGalleryExternalAction = new AbstractAction(L10N.FBF_OPEN_GALLERY_EXTERNAL_ACTION.s(), Icon.EARTH.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onOpenGalleryExternal();
            }
        };
        openGalleryExternalAction.putValue(Action.LARGE_ICON_KEY, Icon.EARTH.I());

        createGalleryAction = new AbstractAction(L10N.FBF_CREATE_GALLERY_ACTION.s(), Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                onCreateGallery();
            }
        };

        toolbarActions.add(openGalleryExternalAction);
    }

    private void initComponents() {
        setLayout(new BorderLayout());

        createTableModel();
        scroller = new JScrollPane(
            galleryTreeTable = new JXTreeTable(treeTableModel)
        );
        galleryTreeTable.setRootVisible(false);
        galleryTreeTable.setShowsRootHandles(false);
        galleryTreeTable.setToggleClickCount(0);
        galleryTreeTable.setLeafIcon(Icon.GALLERY.i());
        galleryTreeTable.setOpenIcon(Icon.GALLERY.i());
        galleryTreeTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        galleryTreeTable.setInheritsPopupMenu(true);
        galleryTreeTable.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent evt) {
                if ((evt.getClickCount() == 2) && (!getFrame().isBusy())) {
                    onOpenGalleryInternally();
                }
            }
        });
        scroller.setComponentPopupMenu(createMenu().getPopupMenu());
        scroller.addComponentListener(new java.awt.event.ComponentAdapter() {
            @Override
            public void componentResized(java.awt.event.ComponentEvent evt) {
                TableUtil.adjustTableColumnSizes(galleryTreeTable, 0, scroller);
            }
        });
        add(scroller, BorderLayout.CENTER);
    }

    private GalleryInfo getCurrentGalleryInfo() {
        final int row = galleryTreeTable.getSelectedRow();
        return (row == -1)? null : info.get(row);
    }

    private void onOpenGalleryExternal() {
        final GalleryInfo inf = getCurrentGalleryInfo();
        if (inf != null) {
            final String galUrl = inf.getGallery().getURLString();
            OSUtil.runBrowser(galUrl);
        }
    }

    private void onCreateGallery() {
        final int row = galleryTreeTable.getSelectedRow();
        FBGalleryDialog dial = new FBGalleryDialog(getFrame(), row, info);
        if (dial.showDialog()) {
            int index = dial.getParentIndex();
            String name = dial.getGalleryName();

            FBGallery parent = null;
            if (index != -1) {
                final GalleryInfo inf = info.get(index);
                parent = inf.getGallery();
            }

            new CreateGalleryThread(parent,  name, dial.getGalleryDate(), 255).start();
            // TODO: security!!!
        }
    }

    private void buildInfo(ArrayList<FBGallery> gals, int level) {
        final StringBuilder b = new StringBuilder();
        for (int i = 0; i < level; i++) {
            b.append("    ");
        }

        for (FBGallery g : gals) {
            GalleryInfo inf = new GalleryInfo(b.toString() + g.getName(), g);
            info.add(inf);

            ArrayList<FBGallery> children = g.getChildren();
            if (!children.isEmpty()) {
                buildInfo(children, level + 1);
            }
        }
    }

    private void createTableModel() {
        treeTableModel = new TreeTableModel() {
            @Override
            public Class<?> getColumnClass(int col) {
                switch (col) {
                    case 1:
                        return Integer.class;
                    default:
                        return String.class;
                }
            }

            @Override
            public int getColumnCount() {
                return headerNames.length;
            }

            @Override
            public String getColumnName(int index) {
                return headerNames[index];
            }

            @Override
            public int getHierarchicalColumn() {
                return 0;
            }

            @Override
            public Object getValueAt(Object obj, int col) {
                if (obj instanceof ArrayList) {
                    return "--";
                } else {
                    if (obj instanceof FBGallery) {
                        FBGallery gal = (FBGallery)obj;

                        switch (col) {
                            case 0:
                                return gal.getName();
                            case 1:
                                return gal.getPictureCount();
                            case 3:
                                return gal.getDate();
                            default:
                                return null;
                        }
                    } else {
                        return null;
                    }
                }
            }

            @Override
            public boolean isCellEditable(Object arg0, int arg1) {
                return false;
            }

            @Override
            public void setValueAt(Object arg0, Object arg1, int arg2) {
            }

            @Override
            public Object getRoot() {
                return fbAccount.getRootGalleries();
            }

            @Override
            public Object getChild(Object parent, int index) {
                if (parent instanceof ArrayList) {
                    return ((ArrayList<?>)parent).get(index);
                } else {
                    if (parent instanceof FBGallery) {
                        return ((FBGallery)parent).getChild(index);
                    } else {
                        return null;
                    }
                }
            }

            @Override
            public int getChildCount(Object parent) {
                if (parent instanceof ArrayList) {
                    return ((ArrayList<?>)parent).size();
                } else {
                    if (parent instanceof FBGallery) {
                        return ((FBGallery)parent).getChildCount();
                    } else {
                        return 0;
                    }
                }
            }

            @Override
            public boolean isLeaf(Object node) {
                if (node instanceof ArrayList) {
                    return ((ArrayList<?>)node).isEmpty();
                } else {
                    if (node instanceof FBGallery) {
                        return ((FBGallery)node).getChildCount() == 0;
                    } else {
                        return true;
                    }
                }
            }

            @Override
            public void valueForPathChanged(TreePath path, Object newValue) {
            }

            @Override
            public int getIndexOfChild(Object parent, Object child) {
                if (parent instanceof ArrayList) {
                    return ((ArrayList<?>)parent).indexOf(child);
                } else {
                    if (parent instanceof FBGallery && child instanceof FBGallery) {
                        return ((FBGallery)parent).getIndexOfChild((FBGallery)child);
                    } else {
                        return 0;
                    }
                }
            }

            @Override
            public void addTreeModelListener(TreeModelListener l) {
            }

            @Override
            public void removeTreeModelListener(TreeModelListener l) {
            }
        };
    }

    private void setDefaultStatusLabel() {
        /*
        FBAccountInfo inf = fbAccount.getInfo();
        if (inf == null) {
            throw new IllegalStateException();
        }

        final StringBuilder b = new StringBuilder("Space used: ");
        b.append(String.format("%s/%s", inf.getQuotaUsed(), inf.getQuotaTotal()));

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                statusLabel.setText(b.toString());
            }
        });
         *
         */
    }

    private void onOpenGalleryInternally() {
        final GalleryInfo inf = getCurrentGalleryInfo();
        if (inf != null) {
            getFrame().onOpenGallery(inf.getGallery());
////            FBGalleryFrame wnd = getProfile().findGalleryWindow(inf.getGallery().getID());
////            if (wnd == null) {
////                wnd = new FBGalleryFrame(getProfile(), inf.getGallery());
////            }
////            wnd.setVisible(true);
        }
    }

    private void updateGalleries() {
        getFrame().startLongOperation();
        new Thread(updateGalleryThread).start();
    }

    private void onUpdatePictures() {
        getFrame().startLongOperation();
        new Thread(updatePicturesThread).start();
    }

    @Override
    public void update() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public JMenu createMenu() {
        return SwingFactory.createMenu(L10N.FBF_GALLERY_MENU_TITLE.s(),
            openGalleryExternalAction,
            null,
            createGalleryAction,
            null,
            updateGalleriesAction,
            updatePicturesAction);
    }

    @Override
    public JMenu getMenu() {
        return (menu == null) ? menu = createMenu() : menu;
    }

    @Override
    public Collection<AbstractAction> getToolBarActions() {
        return toolbarActions;
    }

}
